package com.whjz.utils.mask;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

@Slf4j
public class FieldMaskUtil
{
    // 总计 100 个 * 号， 预计不会有这么长的字段需要掩码, 暂时放这么长， 可以随意增长，或删短
    private final static String MASK_VALUE = "****************************************************************************************************";

    public static String MaskFields(Map oriMap)
    {
        return MaskFields(JSONObject.toJSONString(oriMap));
    }

    public static String MaskFields(String oriString)
    {
        try
        {
            JSONObject result = JSONObject.parseObject(oriString);
            if (null == result)
            {
                return oriString;
            }

            MaskFieldsBase(result);

            return result.toString();
        }
        catch (Exception e)
        {
            log.error("string 转 jsonObject 过程出现异常， 本次不掩码");
            log.error(e.getMessage(), e);
        }
        return oriString;
    }

    private static JSONObject MaskFieldsBase(Map<String, Object> map)
    {
        for (String key : map.keySet())
        {
            Object obj = map.get(key);

            if ( obj instanceof Map)
            {
                MaskFieldsBase((Map)obj);
            }
        }

        JSONObject jsonObject = new JSONObject(map);
        // 身份证掩码
        jsonObject = maskConstantLengthFieldsByFieldMaskable(jsonObject, IdNoFieldMaskImpl.getInstance());
        // 卡号掩码
        jsonObject = maskConstantLengthFieldsByFieldMaskable(jsonObject, CardNoFieldMaskImpl.getInstance());
        // 电话号掩码
        jsonObject = maskConstantLengthFieldsByFieldMaskable(jsonObject, PhoneNumFieldMaskImpl.getInstance());
        // 变长字符串， 比如地址,姓名
        jsonObject = maskVariableLengthFieldsByFieldMaskable(jsonObject, VariableLenFieldMaskImpl.getInstance());

        /*
         * 如果有新添加的掩码策略，在此处添加即可
         *
         */
        return jsonObject;
    }


    /*
    * 固定长度字段掩码使用这个方法处理
    *
     */
    private static JSONObject maskConstantLengthFieldsByFieldMaskable(JSONObject in, IFieldMaskable fieldMaskable)
    {
        if (!checkMaskConditionStep1(fieldMaskable) && !checkMaskConditionStep2(fieldMaskable))
        {
            return in;
        }

        // 通过掩码长度，获取掩码字符串长度
        String maskString = getMaskString(fieldMaskable.getMaskLen());
        for (String fieldName : fieldMaskable.getMaskFields())
        {
            String fieldValue = "";
            if(in.containsKey(fieldName))
            {
                fieldValue = in.getString(fieldName);

                if (!checkMaskConditionStep3(fieldMaskable, fieldValue))
                {
                    continue;
                }

                fieldValue = fieldValue.substring(0, fieldMaskable.getMaskBeginIndex() - 1) + maskString + fieldValue.substring(fieldMaskable.getMaskEndIndex());
                in.put(fieldName,fieldValue);
            }
        }

        return in;
    }

    /*
     * 变长字段掩码使用这个方法处理
     */
    private static JSONObject maskVariableLengthFieldsByFieldMaskable(JSONObject in, IFieldMaskable fieldMaskable)
    {
        // 判断字段掩码开关， 如果为 false 则对该类包含的字段全部不掩码
        if (!checkMaskConditionStep1(fieldMaskable))
        {
            return in;
        }

        for (String fieldName : fieldMaskable.getMaskFields())
        {
            String fieldValue = "";
            if(in.containsKey(fieldName))
            {
                fieldValue = in.getString(fieldName);

                /*************  必须 设置被淹码字符串长度 ************************/
                fieldMaskable.setFieldLen(fieldValue.length());
                /***************************************************************/

                // 如果计算掩码长度返回小于 0 表示掩码参数设置有问题
                if (!checkMaskConditionStep2(fieldMaskable) && !checkMaskConditionStep3(fieldMaskable, fieldValue))
                {
                    continue;
                }

                String maskString = getMaskString(fieldMaskable.getMaskLen());
                fieldValue = fieldValue.substring(0, fieldMaskable.getMaskBeginIndex() - 1) + maskString + fieldValue.substring(fieldMaskable.getMaskEndIndex());
                in.put(fieldName,fieldValue);
            }
        }

        return in;
    }

    /*
    *  返回 false 表示参数设置的有问题， 不做掩码
    *  由于要适配定长字符串， 变长字符串的两种情况，所以拆为 step1 / step2 两个方法
    *
    */
    private static boolean checkMaskConditionStep1(IFieldMaskable fieldMaskable)
    {
        boolean result = true;

        // 判断字段掩码开关， 如果为 false 则对该类包含的字段全部不掩码
        if(fieldMaskable.getMaskFlag() == false)
        {
            log.info("类: [ {} ]  设置为不掩码,本次不进行掩码。", fieldMaskable.getClass().getSimpleName());
            log.info(fieldMaskable.toString());
            result = false;
        }

        // 检查要掩码的字段字符串是否有内容，如果没有则不作掩码
        if(fieldMaskable.getMaskFields().length <= 0)
        {
            // 项目代码用错误日志打印， 这个地方就不退出或者抛异常了
            log.info("类: [ {} ] 要做掩码的字段数据为空， 请检查掩码数组字段, 本次不进行掩码。", fieldMaskable.getClass().getSimpleName());
            log.info(fieldMaskable.toString());
            result = false;
        }

        return result;
    }

    // 返回 false 表示参数设置的有问题， 不做掩码
    private static boolean checkMaskConditionStep2(IFieldMaskable fieldMaskable)
    {
        boolean result = true;
        // 如果计算掩码长度返回小于 0 表示掩码参数设置有问题
        if (fieldMaskable.getMaskLen() < 0)
        {
            // 项目代码用错误日志打印， 这个地方就不退出或者抛异常了
            log.info("经过计算， 类: [ {} ] 相关的掩码参数设置有误, 请检查相关参数设置， 本次不进行掩码。", fieldMaskable.getClass().getSimpleName());
            log.info(fieldMaskable.toString());
            result = false;
        }

        // 此处表示需要掩码的长度，大于了掩码字符串的最大默认值，目前按照默认的最大长度掩码， 同时打印日志信息提示该问题
        if (fieldMaskable.getMaskLen() > MASK_VALUE.length())
        {
            log.info("类: [ {} ] 要做掩码的长度超过了默认值 {} ,请扩展默认掩码字符长度, 本次按照默认最大长度进行掩码。", fieldMaskable.getClass().getSimpleName(), MASK_VALUE.length());
            log.info(fieldMaskable.toString());

            // 此处目前按照默认的最大长度掩码， 只打印日志信息， 不返回 false
        }

        return result;
    }

    /*
     * 返回 false 表示参数设置的有问题， 不做掩码
     * 变长字段掩码前，需要对每个字段情况做判断， 防止出现字符串操作越界
     */
    private static boolean checkMaskConditionStep3(IFieldMaskable fieldMaskable, String fieldValue)
    {
        boolean result = true;
        // 防止类似本来按照正常情况，某字段具有标准长度， 但是在异常情况该字段的值可能传入的过短，
        // 导致掩码串掩码的位置偏离出了正常字符串的长度位置，导致异常。
        if (fieldValue.length() < fieldMaskable.getFieldLen())
        {
            log.info("要掩码的位置: beginIndex [ {} ], endIndex [ {} ],  超出了被掩码值的范围, 该值不掩码, fieldValue=[ {} ], fieldValue.length()=[ {} ] .",
                    fieldMaskable.getMaskBeginIndex(),  fieldMaskable.getMaskEndIndex(), fieldValue, fieldValue.length() );
            result = false;
        }

        // 防止掩码串的长度超过了被淹码字符串的长度， 否则会出异常
        if (fieldValue.length() < fieldMaskable.getMaskLen())
        {
            log.info("要掩码的长度: [ {} ], 大于被掩码字符串: [ {} ] 的长度: [ {} ]， 不对该字符串掩码 ", fieldMaskable.getMaskLen(), fieldValue, fieldValue.length() );
            result = false;
        }

        return  result;
    }


    private static String getMaskString(int len)
    {
        String result = null;
        if (len <= 0)
        {
            result = MASK_VALUE.substring(0, 1);
        }
        else if (len <= MASK_VALUE.length())
        {
            result = MASK_VALUE.substring(0, len);
        }
        else
        {
            result = MASK_VALUE;
        }

        return result;
    }

}
